/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "core/components_ng/pattern/qrcode/qrcode_modifier.h"

namespace OHOS::Ace::NG {
QRCodeModifier::QRCodeModifier()
    : opacity_(AceType::MakeRefPtr<AnimatablePropertyFloat>(1.0f)),
      qrCodeSize_(AceType::MakeRefPtr<PropertyFloat>(0.0f)), value_(AceType::MakeRefPtr<PropertyString>("")),
      paintOffset_(AceType::MakeRefPtr<PropertyOffsetF>(OffsetF())),
      color_(AceType::MakeRefPtr<PropertyColor>(Color::BLACK)),
      backgroundColor_(AceType::MakeRefPtr<PropertyColor>(Color::WHITE))
{
    AttachProperty(opacity_);
    AttachProperty(qrCodeSize_);
    AttachProperty(value_);
    AttachProperty(paintOffset_);
    AttachProperty(color_);
    AttachProperty(backgroundColor_);
}

void QRCodeModifier::onDraw(DrawingContext& context)
{
    auto& canvas = context.canvas;
    auto opacity = opacity_->Get();
    auto qrCodeSize = qrCodeSize_->Get();
    auto value = value_->Get();
    auto paintOffset = paintOffset_->Get();
    auto color = color_->Get();
    auto backgroundColor = backgroundColor_->Get();
    auto qrCode = qrcodegen::QrCode::encodeText(value.c_str(), qrcodegen::QrCode::Ecc::LOW);
    if (!qrCode.getFlag() || qrCode.getSize() == 0 || qrCodeSize <= 0 ||
        qrCodeSize < static_cast<float>(qrCode.getSize())) {
        TAG_LOGE(AceLogTag::ACE_QRCODE, "QRCodeSize is too small. QRCodeSize: %{public}f", qrCodeSize);
        return;
    }
    color = color.BlendOpacity(opacity);
    auto bitMap = CreateBitMap(static_cast<int32_t>(qrCodeSize), qrCode, color, backgroundColor);

    RSImage image;
    image.BuildFromBitmap(bitMap);

    // 1.Calculate the QRcode size user defined, calculate the size of QRcode generated by codegen.
    // 2.Calculate the max block of the QRcode. Ratio = qrCodeSize_ / qrCode.getSize().
    // 3.Scale the qrcode ,the scale ratio is qrCodeSize_ / (blockNumber * qrCode.getSize())

    int32_t blockCounts = static_cast<int32_t>(qrCodeSize) / qrCode.getSize();
    int32_t sizeInPixel = blockCounts * qrCode.getSize();
    if (sizeInPixel == 0) {
        return;
    }
    RSBrush brush;
    brush.SetAntiAlias(true);
    canvas.AttachBrush(brush);
    auto scale = qrCodeSize / sizeInPixel;
    canvas.Save();
    canvas.Scale(scale, scale);
    canvas.DrawImage(image, paintOffset.GetX() / scale, paintOffset.GetY() / scale, RSSamplingOptions());
    canvas.DetachBrush();
    canvas.Restore();
}

RSBitmap QRCodeModifier::CreateBitMap(
    int32_t width, const qrcodegen::QrCode& qrCode, const Color& color, const Color& backgroundColor) const
{
    RSBitmap bitMap;
    RSBitmapFormat colorType = { RSColorType::COLORTYPE_RGBA_8888, RSAlphaType::ALPHATYPE_OPAQUE };
    if (!bitMap.Build(width, width, colorType)) {
        TAG_LOGW(AceLogTag::ACE_QRCODE, "rsBitmap build fail.");
        return bitMap;
    }

    void* rawData = bitMap.GetPixels();
    auto* data = reinterpret_cast<uint32_t*>(rawData);
    int32_t blockWidth = width / qrCode.getSize();
    int32_t maxWidth = 0;
    int32_t maxHeight = 0;
    for (int32_t i = 0; i < width; i++) {
        for (int32_t j = 0; j < width; j++) {
            if (qrCode.getModule(i / blockWidth, j / blockWidth)) {
                data[i * width + j] = ConvertColorFromHighToLow(color);
                maxWidth = i > maxWidth ? i : maxWidth;
                maxHeight = j > maxHeight ? j : maxHeight;
            }
        }
    }
    if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
        return bitMap;
    }
    for (int32_t i = 0; i <= maxWidth; i++) {
        for (int32_t j = 0; j <= maxHeight; j++) {
            if (!qrCode.getModule(i / blockWidth, j / blockWidth)) {
                data[i * width + j] = ConvertColorFromHighToLow(backgroundColor);
            }
        }
    }
    return bitMap;
}

uint32_t QRCodeModifier::ConvertColorFromHighToLow(const Color& color) const
{
    BGRA convertedColor;
    convertedColor.argb.blue = color.GetBlue();
    convertedColor.argb.green = color.GetGreen();
    convertedColor.argb.red = color.GetRed();
    convertedColor.argb.alpha = color.GetAlpha();
    return convertedColor.value;
}

void QRCodeModifier::SetQRCodeOpacity(float opacity)
{
    if (opacity_) {
        opacity_->Set(opacity);
    }
}

void QRCodeModifier::SetQRCodeSize(float qrCodeSize)
{
    if (qrCodeSize_) {
        qrCodeSize_->Set(qrCodeSize);
    }
}

void QRCodeModifier::SetQRCodeValue(const std::string& value)
{
    if (value_) {
        value_->Set(value);
    }
}

void QRCodeModifier::SetPaintOffset(const OffsetF& paintOffset)
{
    if (paintOffset_) {
        paintOffset_->Set(paintOffset);
    }
}

void QRCodeModifier::SetQRCodeColor(const Color& color)
{
    if (color_) {
        color_->Set(color);
    }
}

void QRCodeModifier::SetQRCodeBackgroundColor(const Color& bgColor)
{
    if (backgroundColor_) {
        backgroundColor_->Set(bgColor);
    }
}
} // namespace OHOS::Ace::NG
